package com.lframework.xingyun.sc.impl.stock;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.lframework.starter.common.exceptions.impl.DefaultClientException;
import com.lframework.starter.common.exceptions.impl.DefaultSysException;
import com.lframework.starter.common.utils.Assert;
import com.lframework.starter.common.utils.CollectionUtil;
import com.lframework.starter.common.utils.NumberUtil;
import com.lframework.starter.web.core.impl.BaseMpServiceImpl;
import com.lframework.starter.web.core.components.resp.PageResult;
import com.lframework.starter.web.core.utils.ApplicationUtil;
import com.lframework.starter.web.core.utils.IdUtil;
import com.lframework.starter.web.core.utils.PageHelperUtil;
import com.lframework.starter.web.core.utils.PageResultUtil;
import com.lframework.starter.web.inner.dto.stock.MaterialDto;
import com.lframework.xingyun.basedata.entity.MakeMethodMaterial;
import com.lframework.xingyun.basedata.entity.Product;
import com.lframework.xingyun.basedata.entity.ProductAssistantUnit;
import com.lframework.xingyun.basedata.entity.ProductBundle;
import com.lframework.xingyun.basedata.enums.ProductType;
import com.lframework.xingyun.basedata.service.make.MakeMethodMaterialService;
import com.lframework.xingyun.basedata.service.product.ProductAssistantUnitService;
import com.lframework.xingyun.basedata.service.product.ProductBundleService;
import com.lframework.xingyun.basedata.service.product.ProductPurchaseService;
import com.lframework.xingyun.basedata.service.product.ProductService;
import com.lframework.starter.web.inner.dto.stock.ProductStockChangeDto;
import com.lframework.xingyun.sc.dto.stock.product.ProductStockDto;
import com.lframework.xingyun.sc.entity.ProductStock;
import com.lframework.xingyun.sc.enums.StockAdjustSheetBizType;
import com.lframework.xingyun.sc.events.stock.AddStockEvent;
import com.lframework.xingyun.sc.events.stock.SubStockEvent;
import com.lframework.xingyun.sc.mappers.ProductStockMapper;
import com.lframework.xingyun.sc.service.stock.ProductStockLogService;
import com.lframework.xingyun.sc.service.stock.ProductStockService;
import com.lframework.xingyun.sc.service.stock.adjust.StockAdjustSheetService;
import com.lframework.xingyun.sc.vo.stock.AddProductStockVo;
import com.lframework.xingyun.sc.vo.stock.DecomposeProductStockVo;
import com.lframework.xingyun.sc.vo.stock.QueryProductStockVo;
import com.lframework.xingyun.sc.vo.stock.SubProductStockVo;
import com.lframework.xingyun.sc.vo.stock.adjust.stock.CreateStockAdjustSheetVo;
import com.lframework.xingyun.sc.vo.stock.adjust.stock.StockAdjustProductVo;
import com.lframework.xingyun.sc.vo.stock.log.AddLogWithAddStockVo;
import com.lframework.xingyun.sc.vo.stock.log.AddLogWithSubStockVo;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class ProductStockServiceImpl extends BaseMpServiceImpl<ProductStockMapper, ProductStock>
    implements ProductStockService {

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductBundleService productBundleService;

    @Autowired
    private ProductPurchaseService productPurchaseService;

    @Autowired
    private ProductStockLogService productStockLogService;

    @Autowired
    private MakeMethodMaterialService makeMethodMaterialService;

    @Autowired
    private ProductAssistantUnitService productAssistantUnitService;

    @Override
    public PageResult<ProductStockDto> query(Integer pageIndex, Integer pageSize,
        QueryProductStockVo vo) {

      Assert.greaterThanZero(pageIndex);
      Assert.greaterThanZero(pageSize);

      PageHelperUtil.startPage(pageIndex, pageSize);
      List<ProductStockDto> datas = this.query(vo);

      return PageResultUtil.convert(new PageInfo<>(datas));
    }

    @Override
    public List<ProductStockDto> query(QueryProductStockVo vo) {

      return getBaseMapper().query(vo);
    }

    @Override
    public ProductStock getByProductIdAndScId(String productId, String scId) {

      Product product = productService.findById(productId);
      if (product == null) {
        return null;
      }

      if (Arrays.asList(ProductType.NORMAL, ProductType._SPEC).contains(product.getProductType())) {
        return getBaseMapper().getByProductIdAndScId(productId, scId);
      }
      else if(product.getProductType() == ProductType.BUNDLE) {
        List<ProductBundle> productBundles = productBundleService.getByMainProductId(productId);
        if (CollectionUtil.isEmpty(productBundles)) {
          return null;
        }

        List<String> productIds = productBundles.stream().map(ProductBundle::getProductId).collect(
            Collectors.toList());
        List<ProductStock> productStocks = this.getByProductIdsAndScId(productIds, scId,
            Arrays.asList(ProductType.NORMAL.getCode(),  ProductType._SPEC.getCode()));

        Map<String, BigDecimal> normalStockNum = new HashMap<>();
        for (ProductBundle productBundle : productBundles) {
          BigDecimal stockNum = BigDecimal.ZERO;
          String id = productBundle.getProductId();
          ProductStock productStock = productStocks.stream().filter(t -> t.getProductId().equals(id))
              .findFirst().orElse(null);
          if (productStock == null || productStock.getStockNum().doubleValue() <= 0) {
            // 此处说明有单品没有库存
          } else {
              stockNum = productStock.getStockNum().divide(new BigDecimal(productBundle.getBundleNum()), 6, RoundingMode.HALF_UP);
          }
          normalStockNum.put(id, stockNum);
        }

        ProductStock productStock = new ProductStock();
        productStock.setId(IdUtil.getId());
        productStock.setScId(scId);
        productStock.setProductId(productId);
        productStock.setStockNum(Collections.min(normalStockNum.values()));
        productStock.setTaxPrice(BigDecimal.ZERO);
        productStock.setTaxAmount(BigDecimal.ZERO);

        return productStock;
      }
      return null;
    }

      @Override
      public ProductStock getByProductIdAndScIdAndUnitId(String productId, String scId, String unitId) {
          Product product = productService.findById(productId);
          if (product == null) {
              throw new DefaultClientException("商品不存在");
          }

          if (Arrays.asList(ProductType.NORMAL, ProductType._SPEC).contains(product.getProductType())) {
              return getBaseMapper().getByProductIdAndScIdAndUnitId(productId, scId, unitId);
          }
          else if(product.getProductType() == ProductType.BUNDLE) {
              List<ProductBundle> productBundles = productBundleService.getByMainProductId(productId);
              if (CollectionUtil.isEmpty(productBundles)) {
                  return null;
              }

              List<String> productIds = productBundles.stream().map(ProductBundle::getProductId).collect(
                      Collectors.toList());
              List<ProductStock> productStocks = this.getByProductIdsAndScId(productIds, scId,
                      Arrays.asList(ProductType.NORMAL.getCode(),  ProductType._SPEC.getCode()));

              Map<String, BigDecimal> normalStockNum = new HashMap<>();
              for (ProductBundle productBundle : productBundles) {
                  BigDecimal stockNum = BigDecimal.ZERO;
                  String id = productBundle.getProductId();
                  ProductStock productStock = productStocks.stream().filter(t -> t.getProductId().equals(id))
                          .findFirst().orElse(null);
                  if (productStock == null || productStock.getStockNum().doubleValue() <= 0) {
                      // 此处说明有单品没有库存
                  } else {
                      stockNum = productStock.getStockNum().divide(new BigDecimal(productBundle.getBundleNum()), 6, RoundingMode.HALF_UP);
                  }
                  normalStockNum.put(id, stockNum);
              }

              ProductStock productStock = new ProductStock();
              productStock.setId(IdUtil.getId());
              productStock.setScId(scId);
              productStock.setProductId(productId);
              productStock.setStockNum(Collections.min(normalStockNum.values()));
              productStock.setTaxPrice(BigDecimal.ZERO);
              productStock.setTaxAmount(BigDecimal.ZERO);

              return productStock;
          }
          return null;
      }

    @Override
    public List<ProductStock> getByProductIdsAndScId(List<String> productIds, String scId,
        List<Integer> productTypes) {

      if (CollectionUtil.isEmpty(productIds)) {
        return CollectionUtil.emptyList();
      }

      return getBaseMapper().getByProductIdsAndScId(productIds, scId, productTypes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void decompose(DecomposeProductStockVo vo) {
        String productId = vo.getProductId();

        ProductAssistantUnitService productAssistantUnitService = getThis(ProductAssistantUnitService.class);
        List<ProductAssistantUnit> productAssistantUnits = productAssistantUnitService.queryByProductId(productId);
        String unitId = vo.getUnitId();
        String toUnitId = vo.getToUnitId();
        if (StrUtil.equals(toUnitId, unitId)) {
            throw new DefaultClientException("拆分单位与库存单位相同！");
        }
        List<ProductAssistantUnit> assistantUnits = queryProductAssistantUnit(productAssistantUnits, unitId, toUnitId);
        if (CollectionUtil.isEmpty(assistantUnits)) {
            throw new DefaultClientException("拆分单位与库存单位不存在转换关系！");
        }
        log.info("拆分公式: {}", JSONUtil.toJsonStr(assistantUnits));

        forUnionStockAdjustSheet(assistantUnits, vo.getScId(), productId, BigDecimal.valueOf(vo.getDecomposeNum()));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProductStockChangeDto addStock(AddProductStockVo vo) {

      List<ProductType> productTypes = Arrays.asList(ProductType.NORMAL, ProductType._SPEC);

      Product product = productService.findById(vo.getProductId());
      if (!productTypes.contains(product.getProductType())) {
          throw new DefaultClientException(
              StrUtil.format("只有商品类型为【{}】的商品支持入库！",
                  productTypes.stream()
                      .map(ProductType::getDesc)
                      .collect(Collectors.joining(",")))
          );
      }

      // 单品标识
      Boolean normalFlag = vo.getNormalFlag();
      // 单品添加库存
      if (normalFlag) {
          return execAddStock(vo, product, vo.getStockNum());
      } else {
          return addMaterialStock(vo);
      }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ProductStockChangeDto subStock(SubProductStockVo vo) {

        List<ProductType> productTypes = Arrays.asList(ProductType.NORMAL, ProductType._SPEC);

        Product product = productService.findById(vo.getProductId());
        if (!productTypes.contains(product.getProductType())) {
          throw new DefaultClientException(
            StrUtil.format("只有商品类型为【{}】的商品支持出库！",
                    productTypes.stream()
                    .map(ProductType::getDesc)
                    .collect(Collectors.joining(",")))
          );
        }

        // 单品标识
        Boolean normalFlag = vo.getNormalFlag();
        // 单品扣减库存
        if (normalFlag) {
          return execSubStock(vo, product, vo.getStockNum());
        }
        // 根据原材料扣减库存
        else {
            List<MakeMethodMaterial> makeMethodMaterials = makeMethodMaterialService
                    .selectByCodeAndSkuCode(product.getCode(), product.getSkuCode());
            return subMaterialStock(vo, makeMethodMaterials);
        }
    }

    /**
     * 执行库存添加
     * @param vo
     * @param product
     * @param num
     * @return
     */
    private ProductStockChangeDto execAddStock(AddProductStockVo vo, Product product, BigDecimal num) {
        ProductStock productStock = getProductStock(
                vo.getScId(),
                product.getId(),
                vo.getUnitId(),
                productPurchaseService.getBaseMapper().selectById(product.getId()).getPrice()
        );

        // 如果taxAmount为null，代表不重算均价，即：按当前均价直接出库
        boolean reCalcCostPrice = vo.getTaxPrice() != null;
        if (vo.getTaxPrice() == null) {
            vo.setTaxPrice(NumberUtil.mul(productStock.getTaxPrice(), vo.getStockNum()));
        }

        vo.setTaxPrice(NumberUtil.getNumber(vo.getTaxPrice(), 6));

        int count = getBaseMapper().addStock(vo.getProductId(), vo.getUnitId(), vo.getScId(), vo.getStockNum(),
                NumberUtil.mul(vo.getTaxPrice(), vo.getStockNum()), productStock.getStockNum(),
                productStock.getTaxAmount(), reCalcCostPrice);
        if (count != 1) {
            throw new DefaultClientException(
                    "商品（" + product.getCode() + "）" + product.getName() + "入库失败，请稍后重试！");
        }

        AddLogWithAddStockVo addLogWithAddStockVo = new AddLogWithAddStockVo();
        addLogWithAddStockVo.setProductId(vo.getProductId());
        addLogWithAddStockVo.setUnitId(vo.getUnitId());
        addLogWithAddStockVo.setScId(vo.getScId());
        addLogWithAddStockVo.setStockNum(vo.getStockNum());
        addLogWithAddStockVo.setTaxAmount(NumberUtil.mul(vo.getTaxPrice(), vo.getStockNum()));
        addLogWithAddStockVo.setOriStockNum(productStock.getStockNum());
        addLogWithAddStockVo.setCurStockNum(NumberUtil.add(productStock.getStockNum(), vo.getStockNum()));
        addLogWithAddStockVo.setOriTaxPrice(productStock.getTaxPrice());
        addLogWithAddStockVo.setCurTaxPrice(!reCalcCostPrice ?
                productStock.getTaxPrice() :
                Objects.equals(addLogWithAddStockVo.getCurStockNum(), BigDecimal.ZERO) ?
                        BigDecimal.ZERO :
                        NumberUtil.getNumber(
                                NumberUtil.div(NumberUtil.add(productStock.getTaxAmount(),
                                                NumberUtil.mul(vo.getTaxPrice(), vo.getStockNum())),
                                        addLogWithAddStockVo.getCurStockNum()), 6));
        addLogWithAddStockVo.setCreateTime(vo.getCreateTime());
        addLogWithAddStockVo.setBizId(vo.getBizId());
        addLogWithAddStockVo.setBizDetailId(vo.getBizDetailId());
        addLogWithAddStockVo.setBizCode(vo.getBizCode());
        addLogWithAddStockVo.setBizType(vo.getBizType());

        productStockLogService.addLogWithAddStock(addLogWithAddStockVo);
        ProductStockChangeDto stockChange = new ProductStockChangeDto();
        stockChange.setScId(vo.getScId());
        stockChange.setProductId(StrUtil.format("{}-{}", vo.getProductId(), vo.getUnitId()));
        stockChange.setNum(vo.getStockNum());
        stockChange.setTaxAmount(addLogWithAddStockVo.getTaxAmount());
        stockChange.setCurTaxPrice(addLogWithAddStockVo.getCurTaxPrice());
        stockChange.setCreateTime(vo.getCreateTime());
        stockChange.setCurStockNum(addLogWithAddStockVo.getCurStockNum());

        MaterialDto materialDto = new MaterialDto();
        materialDto.setProductId(vo.getProductId());
        materialDto.setUnitId(vo.getUnitId());
        materialDto.setNum(vo.getStockNum().divide(num, 6, RoundingMode.HALF_UP));
        stockChange.getMaterialDtos().add(materialDto);

        AddStockEvent addStockEvent = new AddStockEvent(this, stockChange);
        ApplicationUtil.publishEvent(addStockEvent);

        return stockChange;
    }

    /**
     * 执行库存扣减
     * @param vo
     * @param product
     * @return
     */
    private ProductStockChangeDto execSubStock(SubProductStockVo vo, Product product, BigDecimal num) {
        ProductStock productStock = getProductStock(
                vo.getScId(),
                product.getId(),
                vo.getUnitId(),
                productPurchaseService.getBaseMapper().selectById(product.getId()).getPrice()
        );

        // 如果taxAmount为null，代表不重算均价，即：按当前均价直接出库
        boolean reCalcCostPrice = vo.getTaxAmount() != null;
        if (vo.getTaxAmount() == null) {
            vo.setTaxAmount(NumberUtil.mul(productStock.getTaxPrice(), vo.getStockNum()));
        }

        vo.setTaxAmount(NumberUtil.getNumber(vo.getTaxAmount(), 6));

        BigDecimal subTaxAmount = vo.getTaxAmount();

        int count = getBaseMapper().subStock(vo.getProductId(), vo.getUnitId(), vo.getScId(), vo.getStockNum(),
                subTaxAmount,
                productStock.getStockNum(), productStock.getTaxAmount(), reCalcCostPrice);
        if (count != 1) {
            throw new DefaultClientException(
                    "商品（" + product.getCode() + "）" + product.getName() + "出库失败，请稍后重试！");
        }

        AddLogWithSubStockVo addLogWithAddStockVo = new AddLogWithSubStockVo();
        addLogWithAddStockVo.setProductId(vo.getProductId());
        addLogWithAddStockVo.setUnitId(vo.getUnitId());
        addLogWithAddStockVo.setScId(vo.getScId());
        addLogWithAddStockVo.setStockNum(vo.getStockNum());
        addLogWithAddStockVo.setTaxAmount(subTaxAmount);
        addLogWithAddStockVo.setOriStockNum(productStock.getStockNum());
        addLogWithAddStockVo.setCurStockNum(NumberUtil.sub(productStock.getStockNum(), vo.getStockNum()));
        addLogWithAddStockVo.setOriTaxPrice(productStock.getTaxPrice());
        addLogWithAddStockVo.setCurTaxPrice(!reCalcCostPrice ?
                productStock.getTaxPrice() :
                Objects.equals(addLogWithAddStockVo.getCurStockNum(), BigDecimal.ZERO) ?
                        BigDecimal.ZERO :
                        NumberUtil.getNumber(
                                NumberUtil.div(NumberUtil.sub(productStock.getTaxAmount(), subTaxAmount),
                                        addLogWithAddStockVo.getCurStockNum()), 6));
        addLogWithAddStockVo.setCreateTime(vo.getCreateTime());
        addLogWithAddStockVo.setBizId(vo.getBizId());
        addLogWithAddStockVo.setBizDetailId(vo.getBizDetailId());
        addLogWithAddStockVo.setBizCode(vo.getBizCode());
        addLogWithAddStockVo.setBizType(vo.getBizType());

        productStockLogService.addLogWithSubStock(addLogWithAddStockVo);

        ProductStockChangeDto stockChange = new ProductStockChangeDto();
        stockChange.setScId(vo.getScId());
        stockChange.setProductId(StrUtil.format("{}-{}", vo.getProductId(), vo.getUnitId()));
        stockChange.setNum(vo.getStockNum());
        stockChange.setTaxAmount(subTaxAmount);
        stockChange.setCurTaxPrice(addLogWithAddStockVo.getCurTaxPrice());
        stockChange.setCreateTime(vo.getCreateTime());
        stockChange.setCurStockNum(addLogWithAddStockVo.getCurStockNum());

        MaterialDto materialDto = new MaterialDto();
        materialDto.setProductId(vo.getProductId());
        materialDto.setUnitId(vo.getUnitId());
        materialDto.setNum(vo.getStockNum().divide(num, 6, RoundingMode.HALF_UP));
        stockChange.getMaterialDtos().add(materialDto);


        SubStockEvent subStockEvent = new SubStockEvent(this, stockChange);
        ApplicationUtil.publishEvent(subStockEvent);

        return stockChange;
    }

    /**
     * 根据原材料添加库存
     * @param vo
     * @return
     */
    private ProductStockChangeDto addMaterialStock(AddProductStockVo vo) {
        ProductStockChangeDto productStockChangeDto = new ProductStockChangeDto();
        productStockChangeDto.setScId(vo.getScId());
        productStockChangeDto.setProductId(vo.getProductId());
        productStockChangeDto.setNum(vo.getStockNum());
        productStockChangeDto.setTaxAmount(BigDecimal.ZERO);
        productStockChangeDto.setCurTaxPrice(BigDecimal.ZERO);
        productStockChangeDto.setCurStockNum(BigDecimal.ZERO);
        productStockChangeDto.setCreateTime(DateUtil.date().toLocalDateTime());

        BigDecimal inputStockNum = vo.getStockNum();
        List<MaterialDto> stockChangeDetail = vo.getStockChangeDetail();
        if (CollUtil.isNotEmpty(stockChangeDetail)) {
            for (MaterialDto materialDto : stockChangeDetail) {
                Product product = productService.findById(materialDto.getProductId());
                vo.setProductId(materialDto.getProductId());
                vo.setUnitId(materialDto.getUnitId());
                vo.setStockNum(materialDto.getNum().multiply(inputStockNum));
                vo.setTaxPrice(null);
                ProductStockChangeDto dto = execAddStock(vo, product, inputStockNum);
                productStockChangeDto.setTaxAmount(productStockChangeDto.getTaxAmount().add(dto.getTaxAmount()));
                productStockChangeDto.setCurTaxPrice(productStockChangeDto.getCurTaxPrice().add(dto.getCurTaxPrice()));
                productStockChangeDto.getMaterialDtos().addAll(dto.getMaterialDtos());
            }
        }

        return productStockChangeDto;
    }

    /**
     * 根据原材料扣减库存
     * @param vo
     * @param makeMethodMaterials
     * @return
     */
    private ProductStockChangeDto subMaterialStock(SubProductStockVo vo, List<MakeMethodMaterial> makeMethodMaterials) {
        // 商品制作材料组
        Map<String, List<MakeMethodMaterial>> makeMethodMaterialsGroup = makeMethodMaterials.stream()
                .filter(item -> item.getType() == 0 && StrUtil.isAllBlank(item.getMainCode(), item.getMainSkuCode()))
                .collect(Collectors.groupingBy(item -> StrUtil.format("{}/{}/{}", item.getCode(), item.getSkuCode(), item.getConsumeUnitId())));
        ProductStockChangeDto productStockChangeDto = new ProductStockChangeDto();
        productStockChangeDto.setScId(vo.getScId());
        productStockChangeDto.setProductId(vo.getProductId());
        productStockChangeDto.setNum(vo.getStockNum());
        productStockChangeDto.setTaxAmount(BigDecimal.ZERO);
        productStockChangeDto.setCurTaxPrice(BigDecimal.ZERO);
        productStockChangeDto.setCurStockNum(BigDecimal.ZERO);
        productStockChangeDto.setCreateTime(DateUtil.date().toLocalDateTime());
        // 出库数量
        BigDecimal outStockNum = vo.getStockNum();
        for (Map.Entry<String, List<MakeMethodMaterial>> entry : makeMethodMaterialsGroup.entrySet()) {
            boolean isExecSubStock = false;
            List<MakeMethodMaterial> materials = entry.getValue();
            MakeMethodMaterial makeMethodMaterial = materials.get(0);
            // 主材料商品信息
            Product productData = productService.lambdaQuery()
                    .eq(Product::getCode, makeMethodMaterial.getCode())
                    .eq(Product::getSkuCode, makeMethodMaterial.getSkuCode())
                    .isNull(Product::getShopId).one();
            // 消耗单位
            String consumeUnitId = makeMethodMaterial.getConsumeUnitId();
            // 用量
            BigDecimal consumeNum = materials.stream()
                    .map(MakeMethodMaterial::getConsumeNum)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .multiply(outStockNum);
            // 计算扣减库存
            ProductStock productStock = findProductStock(vo.getScId(), makeMethodMaterial, consumeNum);
            // 主材料
            if (Objects.nonNull(productStock)) {
                // 库存单位
                String unitId = productStock.getUnitId();
                // 比较库存单位与消耗单位是否一致
                if (!StrUtil.equals(unitId, consumeUnitId)) {
                    // 不一致 需要对单位进行拆分
                    Integer decomposeNum = productAssistantUnitService
                            .calcDecomposeNum(productStock.getProductId(), unitId, consumeUnitId, consumeNum);
                    DecomposeProductStockVo decomposeProductStockVo = new DecomposeProductStockVo();
                    decomposeProductStockVo.setScId(vo.getScId());
                    decomposeProductStockVo.setProductId(productStock.getProductId());
                    decomposeProductStockVo.setUnitId(unitId);
                    decomposeProductStockVo.setToUnitId(consumeUnitId);
                    decomposeProductStockVo.setDecomposeNum(decomposeNum);
                    getThis(this.getClass()).decompose(decomposeProductStockVo);
                }
                vo.setProductId(productData.getId());
                vo.setUnitId(consumeUnitId);
                vo.setStockNum(consumeNum);
                vo.setTaxAmount(null);
                ProductStockChangeDto dto = execSubStock(vo, productData, outStockNum);
                productStockChangeDto.setTaxAmount(productStockChangeDto.getTaxAmount().add(dto.getTaxAmount()));
                productStockChangeDto.setCurTaxPrice(productStockChangeDto.getCurTaxPrice().add(dto.getCurTaxPrice()));
                productStockChangeDto.getMaterialDtos().addAll(dto.getMaterialDtos());
                isExecSubStock = true;
            }
            // 没有主材料库存使用替代材料
            else {
                // 替换原料
                Map<String, List<MakeMethodMaterial>> replaceMaterialsGroup = makeMethodMaterials.stream()
                        .filter(item -> item.getType() == 1 &&
                                StrUtil.equals(item.getMainCode(), makeMethodMaterial.getCode()) &&
                                StrUtil.equals(item.getMainSkuCode(), makeMethodMaterial.getSkuCode()))
                        .collect(Collectors.groupingBy(item -> StrUtil.format("{}/{}/{}", item.getCode(), item.getSkuCode(), item.getConsumeUnitId())));
                for (Map.Entry<String, List<MakeMethodMaterial>> replaceEntry : replaceMaterialsGroup.entrySet()) {
                    if (!isExecSubStock) {
                        List<MakeMethodMaterial> replaceMaterials = replaceEntry.getValue();
                        MakeMethodMaterial replaceMakeMethodMaterial = replaceMaterials.get(0);
                        // 替换材料商品信息
                        Product replaceProductData = productService.lambdaQuery()
                                .eq(Product::getCode, replaceMakeMethodMaterial.getCode())
                                .eq(Product::getSkuCode, replaceMakeMethodMaterial.getSkuCode())
                                .isNull(Product::getShopId).one();
                        // 替换材料消耗单位
                        String replaceConsumeUnitId = replaceMakeMethodMaterial.getConsumeUnitId();
                        // 替换材料用量
                        BigDecimal replaceConsumeNum = replaceMaterials.stream()
                                .map(MakeMethodMaterial::getConsumeNum)
                                .reduce(BigDecimal.ZERO, BigDecimal::add)
                                .multiply(outStockNum);
                        // 替换品库存
                        ProductStock replaceProductStock = findProductStock(vo.getScId(), replaceMakeMethodMaterial, replaceConsumeNum);
                        // 替换材料
                        if (Objects.nonNull(replaceProductStock)) {
                            // 库存单位
                            String unitId = replaceProductStock.getUnitId();
                            // 比较库存单位与消耗单位是否一致
                            if (!StrUtil.equals(unitId, replaceConsumeUnitId)) {
                                // 不一致 需要对单位进行拆分
                                Integer decomposeNum = productAssistantUnitService
                                        .calcDecomposeNum(replaceProductStock.getProductId(), unitId, replaceConsumeUnitId, replaceConsumeNum);
                                DecomposeProductStockVo decomposeProductStockVo = new DecomposeProductStockVo();
                                decomposeProductStockVo.setScId(vo.getScId());
                                decomposeProductStockVo.setProductId(replaceProductStock.getProductId());
                                decomposeProductStockVo.setUnitId(unitId);
                                decomposeProductStockVo.setToUnitId(replaceConsumeUnitId);
                                decomposeProductStockVo.setDecomposeNum(decomposeNum);
                                ProductStock befProductStock = getProductStock(
                                        vo.getScId(),
                                        decomposeProductStockVo.getProductId(),
                                        decomposeProductStockVo.getToUnitId(),
                                        productPurchaseService.getBaseMapper().selectById(decomposeProductStockVo.getProductId()).getPrice()
                                );
                                log.info("拆分前,商品ID: {}, 单位ID: {}, 库存数量: {}",
                                        befProductStock.getProductId(),
                                        befProductStock.getUnitId(),
                                        befProductStock.getStockNum());
                                getThis(this.getClass()).decompose(decomposeProductStockVo);
                                ProductStock atProductStock = getProductStock(
                                        vo.getScId(),
                                        decomposeProductStockVo.getProductId(),
                                        decomposeProductStockVo.getToUnitId(),
                                        productPurchaseService.getBaseMapper().selectById(decomposeProductStockVo.getProductId()).getPrice()
                                );
                                log.info("拆分后,商品ID: {}, 单位ID: {}, 库存数量: {}",
                                        atProductStock.getProductId(),
                                        atProductStock.getUnitId(),
                                        atProductStock.getStockNum());
                            }
                            vo.setProductId(replaceProductData.getId());
                            vo.setUnitId(replaceConsumeUnitId);
                            vo.setStockNum(replaceConsumeNum);
                            vo.setTaxAmount(null);
                            ProductStockChangeDto dto = execSubStock(vo, replaceProductData, outStockNum);
                            productStockChangeDto.setTaxAmount(productStockChangeDto.getTaxAmount().add(dto.getTaxAmount()));
                            productStockChangeDto.setCurTaxPrice(productStockChangeDto.getCurTaxPrice().add(dto.getCurTaxPrice()));
                            productStockChangeDto.getMaterialDtos().addAll(dto.getMaterialDtos());
                            isExecSubStock = true;
                        }
                    }
                }
            }

            //没有任何可用扣减库存
            if(!isExecSubStock) {
                // 强制扣减主材料库存
                vo.setProductId(productData.getId());
                vo.setUnitId(consumeUnitId);
                vo.setStockNum(consumeNum);
                vo.setTaxAmount(null);
                ProductStockChangeDto dto = execSubStock(vo, productData, outStockNum);
                productStockChangeDto.setTaxAmount(productStockChangeDto.getTaxAmount().add(dto.getTaxAmount()));
                productStockChangeDto.setCurTaxPrice(productStockChangeDto.getCurTaxPrice().add(dto.getCurTaxPrice()));
                productStockChangeDto.getMaterialDtos().addAll(dto.getMaterialDtos());
            }
        }
        return productStockChangeDto;
    }

    /**
     * 查找原料库存
     * @param scId 仓库id
     * @param makeMethodMaterial 关联材料
     * @param consumeNum 用量
     * @return
     */
    private ProductStock findProductStock(String scId, MakeMethodMaterial makeMethodMaterial, BigDecimal consumeNum) {
        // 材料商品信息
        Product productData = productService.lambdaQuery()
                .eq(Product::getCode, makeMethodMaterial.getCode())
                .eq(Product::getSkuCode, makeMethodMaterial.getSkuCode())
                .isNull(Product::getShopId).one();
        // 材料商品所有单位集合
        Set<String> productUnitSet = new LinkedHashSet<>();
        productUnitSet.add(productData.getUnit());
        // 材料商品辅助单位信息
        List<ProductAssistantUnit> productAssistantUnits = productAssistantUnitService.queryByProductId(productData.getId());
        productAssistantUnits
                .forEach(productUnit -> productUnitSet.add(productUnit.getToUnitId()));
        // 单位库存集合
        Map<String, ProductStock> unitProductStock = new HashMap<>();
        for (String unitId : productUnitSet) {
            unitProductStock.put(unitId, getProductStock(scId, productData.getId(), unitId, makeMethodMaterial.getConsumeCost()));
        }
        // 消耗单位
        String consumeUnitId = makeMethodMaterial.getConsumeUnitId();

        // 计算扣减库存
        return calcProductStock(unitProductStock, productAssistantUnits, consumeUnitId, consumeNum);
    }

    private ProductStock calcProductStock(
            Map<String, ProductStock> unitProductStock,
            List<ProductAssistantUnit> productAssistantUnits,
            String consumeUnitId,
            BigDecimal consumeNum
        ) {
        ProductStock productStock = unitProductStock.get(consumeUnitId);

        if(productStock.getStockNum().compareTo(consumeNum) > 0) {
            return productStock;
        }
        // 库存不足
        else {
            // 查找辅助单位
            ProductAssistantUnit assistantUnit = productAssistantUnits.stream()
                    .filter(i -> StrUtil.equals(i.getToUnitId(), consumeUnitId))
                    .findFirst().orElse(null);
            if (Objects.nonNull(assistantUnit)) {
                // 辅助单位用量
                BigDecimal assistantUnitConsumeNum = consumeNum.divide(assistantUnit.getUnitVal(), 6, RoundingMode.HALF_UP);
                return calcProductStock(unitProductStock, productAssistantUnits, assistantUnit.getAssistantUnitId(), assistantUnitConsumeNum);
            }
        }
        return null;
    }

    /**
     * 获取商品库存
     * @param scId 仓库id
     * @param productId 商品id
     * @param unitId 单位id
     * @param purchasePrice 采购价
     * @return
     */
    private ProductStock getProductStock(String scId, String productId, String unitId, BigDecimal purchasePrice) {
        ProductStock productStock = lambdaQuery().eq(ProductStock::getScId, scId).eq(ProductStock::getProductId, productId).eq(ProductStock::getUnitId, unitId)
                .one();
        if (Objects.isNull(productStock)) {
            productStock = new  ProductStock();
            productStock.setScId(scId);
            productStock.setProductId(productId);
            productStock.setUnitId(unitId);
            productStock.setStockNum(BigDecimal.ZERO);
            productStock.setTaxPrice(purchasePrice);
            productStock.setTaxAmount(BigDecimal.ZERO);
            save(productStock);
        }
        return productStock;
    }

    private void forUnionStockAdjustSheet(List<ProductAssistantUnit> assistantUnits, String scId, String productId, BigDecimal num) {
        for (ProductAssistantUnit productAssistantUnit : assistantUnits) {
            num = unionStockAdjustSheet(productAssistantUnit,  scId, productId, num);
        }
    }

    private BigDecimal unionStockAdjustSheet(ProductAssistantUnit assistantUnit, String scId, String productId, BigDecimal num) {
        subStockAdjustSheet(assistantUnit, scId, productId, num);
        BigDecimal endNum = assistantUnit.getUnitVal().multiply(num);
        addStockAdjustSheet(assistantUnit, scId, productId, endNum);
        return endNum;
    }

    private void subStockAdjustSheet(ProductAssistantUnit assistantUnit, String scId, String productId, BigDecimal num) {
        StockAdjustProductVo stockAdjustProductVo = new StockAdjustProductVo();
        stockAdjustProductVo.setProductId(productId);
        stockAdjustProductVo.setUnitId(assistantUnit.getAssistantUnitId());
        stockAdjustProductVo.setStockNum(num);

        CreateStockAdjustSheetVo createStockAdjustSheetVo = new CreateStockAdjustSheetVo();
        createStockAdjustSheetVo.setProducts(Collections.singletonList(stockAdjustProductVo));
        createStockAdjustSheetVo.setScId(scId);
        createStockAdjustSheetVo.setBizType(StockAdjustSheetBizType.OUT.getCode());
        createStockAdjustSheetVo.setReasonId("3");    //默认统一原因 系统拆分
        createStockAdjustSheetVo.setDescription("库存单位拆分");
        getThis(StockAdjustSheetService.class).directApprovePass(createStockAdjustSheetVo);
    }

    private void addStockAdjustSheet(ProductAssistantUnit assistantUnit, String scId, String productId, BigDecimal num) {
        StockAdjustProductVo stockAdjustProductVo = new StockAdjustProductVo();
        stockAdjustProductVo.setProductId(productId);
        stockAdjustProductVo.setUnitId(assistantUnit.getToUnitId());
        stockAdjustProductVo.setStockNum(num);

        CreateStockAdjustSheetVo createStockAdjustSheetVo = new CreateStockAdjustSheetVo();
        createStockAdjustSheetVo.setProducts(Collections.singletonList(stockAdjustProductVo));
        createStockAdjustSheetVo.setScId(scId);
        createStockAdjustSheetVo.setBizType(StockAdjustSheetBizType.IN.getCode());
        createStockAdjustSheetVo.setReasonId("3");    //默认统一原因 系统拆分
        createStockAdjustSheetVo.setDescription("库存单位拆分");
        getThis(StockAdjustSheetService.class).directApprovePass(createStockAdjustSheetVo);
    }

    /**
     * 查找辅助单位转换公式
     * @param productAssistantUnits
     * @param beginUnitId
     * @param endUnitId
     * @return
     */
    private List<ProductAssistantUnit> queryProductAssistantUnit(List<ProductAssistantUnit> productAssistantUnits, String beginUnitId, String endUnitId) {
        List<ProductAssistantUnit> list = new ArrayList<>();
        queryProductAssistantUnitByBeginUnitIdAndEndUnitId(list, productAssistantUnits, beginUnitId, endUnitId);
        Collections.reverse(list);
        return list;
    }

    private void queryProductAssistantUnitByBeginUnitIdAndEndUnitId(
            List<ProductAssistantUnit> res,
            List<ProductAssistantUnit> productAssistantUnits,
            String beginUnitId,
            String endUnitId) {
        if (CollUtil.isNotEmpty(productAssistantUnits)) {
            ProductAssistantUnit productAssistantUnit = getProductAssistantUnit(productAssistantUnits, endUnitId);
            if (Objects.nonNull(productAssistantUnit)) {
                res.add(productAssistantUnit);
                String assistantUnitId = productAssistantUnit.getAssistantUnitId();
                if (!StrUtil.equals(assistantUnitId, beginUnitId)) {
                    queryProductAssistantUnitByBeginUnitIdAndEndUnitId(res, productAssistantUnits, beginUnitId, assistantUnitId);
                }
            }
        }
    }

    private ProductAssistantUnit getProductAssistantUnit(List<ProductAssistantUnit> productAssistantUnits, String unitId) {
        for (ProductAssistantUnit productAssistantUnit : productAssistantUnits) {
            if (StrUtil.equals(productAssistantUnit.getToUnitId(), unitId)) {
              return productAssistantUnit;
            }
        }
        return null;
    }
}
